<?php
/**
 * TinyMVC - TinyMVCFrontController.class.php file
 *
 * @author Adrian Wieczorek <adi(at)tinymvc(dot)org>
 * @package TinyMVC
 * @version $Id: TinyMVCFrontController.class.php 86 2010-12-17 20:45:44Z adi3ek $
 *
 */

/**
 * TinyMVCFrontController class
 *
 * This is default TinyMVC Framework front controller which handle all requests.
 * It can be instantiated in {@link index.php} bootstrap file, for example:
 *
 * <code>
 * <?php
 *  $frontController = new TinyMVCFrontController(TinyMVCHttpRequest::getInstance(), new TinyMVCViewRenderer);
 *
 *  $frontController->doService();
 * ?>
 * </code>
 *
 * @package TinyMVC
 */
class TinyMVCFrontController {

	/**
	 * @var TinyMVCActionController
	 */
	protected $actionController;
	/**
	 * @var TinyMVCViewRenderer
	 */
	protected $viewRenderer;
	/**
	 * @var TinyMVCRequest
	 */
	protected $request;
	/**
	 * @var string
	 */
	protected $defaultModuleName;
	/**
	 * @var string
	 */
	protected $defaultActionName;
  /*
   * $var array
   */
  protected $plugins = array();

	/**
	 * @param TinyMVCRequest $request Request object
	 * @param TinyMVCViewRenderer $viewRenderer View renderer object
	 * @param string $defaultModuleName Default module name
	 * @param string $defaultActionName Default action name
	 */
	public function __construct(TinyMVCRequest $request, TinyMVCViewRenderer $viewRenderer, $defaultModuleName = 'index', $defaultActionName = 'default') {
		$this->request = $request;
		$this->viewRenderer = $viewRenderer;
		$this->defaultModuleName = $defaultModuleName;
		$this->defaultActionName = $defaultActionName;
	}

 /**
  * process request
  */
	public function doService() {
		foreach ($this->plugins as $pl){
			$pl->preDispatch($this->request);
		}
    $modelAndView = $this->getActionController()->doAction();

		$this->viewRenderer->render($modelAndView);

    foreach ($this->plugins as $pl){
      $pl->afterDispatch($this->request);
    }
	}

 /**
  * resolves and returns correct action controller for module
  * @return TinyMVCActionController Action controller
  */
 protected function getActionController() {
		if(!$this->request->getParam('mod')) {
			$this->request->setParam('mod', $this->defaultModuleName);
		}
		$moduleName = strtolower($this->request->getParam('mod'));

		if(!empty($moduleName)) {
			if(preg_match("/^[a-z_]{1,}$/i", $moduleName) != 0) {
				$actionControllerClassFile = TinyMVC::getInstance()->getControllersPathResolver()->resolve(ucfirst($moduleName));
			}
			else {
				throw new TinyMVCException("Invalid characters in module name: $moduleName");
			}

			if(file_exists($actionControllerClassFile)) {
				require_once($actionControllerClassFile);
				$controllerClassName = ucfirst($moduleName) . 'ActionController';
			}
			else {
				throw new TinyMVCException("ActionController for module: $moduleName - not found.");
			}

			// TODO: pass alternate model here, set modelAndView
			$controller = new $controllerClassName(new TinyMVCModelAndView(new TinyMVCView(TinyMVC::getInstance()->getViewsPathResolver(), ''), null), $this->request, $this->defaultActionName);
			if($controller instanceof TinyMVCActionController) {
				if(($controller->getModelAndView() == null) || ($controller->getRequest() == null)) {
					throw new TinyMVCException("parent::__construct() has not been called in $controllerClassName::__construct()");
				}
			}
			else {
				throw new TinyMVCException(get_class($controller) . " must extends TinyMVCActionController");
			}
			return $controller;
		}
		else {
			throw new TinyMVCException("Unknown module name");
		}
	}

	public function getDefaultModuleName() {
		return $this->defaultModuleName;
	}

	public function setDefaultModuleName($moduleName) {
		$this->defaultModuleName = $moduleName;
	}

	public function getDefaultActionName() {
		return $this->defaultActionName;
	}

	public function setDefaultActionName($actionName) {
		$this->defaultActionName = $actionName;
	}

	public function getRequest() {
		return $this->request;
	}

	public function setRequest(TinyMVCRequest $request) {
		$this->request = $request;
	}

	public function registerPlugin(TinyMVCControllerPluginInterface $plugin){
    $this->plugins[] = $plugin;
  }
}
